use std::path::{Path, PathBuf};
use std::sync::Arc;
use tokio::sync::Mutex;
use tracing::info;
use crate::app_state::AppState;
// use anyhow result
use anyhow::{Context, Result};
use futures::future::ok;
use lazy_static::lazy_static;
use regex::Regex;
use sea_orm::ActiveValue::Set;
use sea_orm::{ActiveModelTrait, ConnectionTrait, EntityTrait, Statement};
use crate::entity::prelude::PrjSystem;
use crate::entity::prj_system;
use crate::repo::point_repo::DbContextHolder;
use crate::util::str_util::to_table_name;

lazy_static! {
    static ref SUFFIX_REGEX: Regex = Regex::new(r"([^\\.]+)").unwrap();
}

pub async fn huang_ling_task(app_state: Arc<Mutex<AppState>>) -> Result<()> {
    // 获取当前项目路径
    let current_dir = std::env::current_dir().unwrap();
    info!("当前项目路径: {:?}", current_dir);
    // 拼接points 文件夹路径
    let points_dir = current_dir.join("points1");
    info!("points 文件夹路径: {:?}", points_dir);

    // 获取 system 表数据数量为0 执行 gen_sys_tree_by_folder
    let system_count;
    {
        let app_state = app_state.lock().await;
        let db_context_holder = app_state.system_repo.get_db_context(1);
        let db_context = db_context_holder;
        system_count = PrjSystem::find().all(db_context).await.context(format!("查询系统表数量失败"))?;
    }
    if system_count.len() == 0 {
        gen_sys_tree_by_folder(app_state.clone(), points_dir, 1, None).await?;
    }

    Ok(())
}

#[async_recursion::async_recursion] // 加这个可让 async fn 函数 递归调用自己,需要cargo add async-recursion
pub async fn gen_sys_tree_by_folder(app_state: Arc<Mutex<AppState>>, points_dir: PathBuf, machine_index: i32, parent_id: Option<i32>) -> Result<()> {
    // 读取文件夹下的所有文件夹和文件
    let (child_dirs, child_files): (Vec<_>, Vec<_>) = std::fs::read_dir(points_dir)?
        .map(|entry| entry.map_err(|e| anyhow::anyhow!("path 路径下的文件/夹 无法读取: {}", e)))
        .collect::<Result<Vec<_>, _>>()?
        .into_iter()
        .map(|entry| entry.path())
        .partition(|path| path.is_dir());
    let child_files: Vec<_> = child_files
        .into_iter()
        .filter(|path| path.extension().map_or(false, |ext| ext == "txt"))
        .collect();

    // 遍历文件夹 ,构造系统树
    for (index, child_dir) in child_dirs.iter().enumerate() {
        let child_dir_name = child_dir.file_name().unwrap().to_str().unwrap();
        info!("child_dir_name: {}", child_dir_name);
        let add_system_id;
        {
            let mut app_state = app_state.lock().await;
            let system_entity = prj_system::ActiveModel {
                name: Set(Some(child_dir_name.to_string())),
                parent_id: Set(parent_id),
                ..Default::default()
            };
            let add_system = system_entity.insert(app_state.system_repo.get_db_context(machine_index as u32)).await.context(format!("插入系统表失败"))?;
            add_system_id = add_system.id;
        }
        gen_sys_tree_by_folder(app_state.clone(), child_dir.to_path_buf(), machine_index, Some(add_system_id)).await?;
    }
    // 遍历 txt 文件
    for child_file in child_files {
        let file_name = child_file.file_name().ok_or(anyhow::anyhow!("文件名无法读取"))?.to_str().ok_or(anyhow::anyhow!("文件名无法转换为字符串"))?;
        info!("读取文件名为: {}", file_name);
        let mut point_names = read_codes_from_file(&child_file).await.context(format!("读取文件内容失败"))?;
        point_names = point_names.iter().map(|x| {
            let mut ret: String;
            // 匹配一个或多个连续的非点字符  ([^\\.]+)
            if SUFFIX_REGEX.is_match(x) {
                ret = SUFFIX_REGEX.captures(x).unwrap().get(0).unwrap().as_str().to_string()
            } else {
                ret = x.clone()
            }
            // to table name
            ret = to_table_name(ret);
            let prefix = match machine_index {
                2 => "dcs02_",
                3 => "dcsfw_",
                _ => "dcs01_",
            };
            let ret = format!("{}{}", prefix, ret);
            return ret;
        }).collect();
        let mut points = vec![];
        {
            let mut app_state = app_state.lock().await;
            points = app_state.point_repo.get_points_by_names_ignore_case(machine_index as u32, point_names.clone()).await.context(format!("查询点表失败"))?;
            if points.len() != point_names.len() {
                let mut point_names = point_names.clone();
                for point in points.iter() {
                    point_names.retain(|x| match &point.base.name {
                        Some(name) => x != name,
                        None => true,
                    });
                }
                info!("没有找到的点: {:?}", point_names);
            }

            // 构造 system_entity
            let file_stem = child_file.file_stem().ok_or(anyhow::anyhow!("文件名无法读取"))?.to_str().ok_or(anyhow::anyhow!("文件名无法转换为字符串"))?;
            let system_entity = prj_system::ActiveModel {
                name: Set(Some(file_stem.to_string())),
                parent_id: Set(parent_id),
                ..Default::default()
            };
            info!("system_entity: {:?}", system_entity);

            let add_system = system_entity.insert(app_state.system_repo.get_db_context(machine_index as u32)).await.context(format!("插入系统表失败"))?;
            let add_system_id = add_system.id;
            if points.len() == 0 {
                continue;
            }
            // 添加 system_id 和 point_id 到 prj_system_point 表里面
            let mut values_str = String::new();
            for point in &points {
                values_str.push_str(&format!("({}, {}),", add_system_id, point.base.id));
            }
            values_str.pop(); // 移除最后一个逗号

            let sql = Statement::from_string(
                sea_orm::DatabaseBackend::MySql,
                format!(
                    "INSERT INTO pointprjsystem (SystemsId, PointsId) VALUES {}",
                    values_str
                ),
            );
            app_state.system_repo.get_db_context(machine_index as u32).execute(sql.clone()).await.context(format!("批量插入系统点表失败, sql: {}", sql))?;
        }
    }


    Ok(())
}

pub async fn read_codes_from_file(file_path: &Path) -> Result<Vec<String>> {
    let file_content = tokio::fs::read_to_string(file_path).await?;
    let lines: Vec<String> = file_content.split("\n").map(|s| s.to_string()).collect();

    let codes: Vec<String> = lines.iter().filter_map(|line| {
        match line.split("\t").next() {
            Some(code) => Some(code.to_string()),
            None => {
                println!("这行内容格式有问题: {}", line);
                None
            }
        }
    }).collect();

    Ok(codes)
}